#include <stdio.h>
#include <stdlib.h>

/*
 * 通过指针变量修改值
 * */
static void modifyVar(int* p)
{
	// 此处指针变量对应的地址和外部的取到的指针地址相同
	printf("In modifyVar *p = %p\n",p);
	*p = 300;
}
void add(int a, int b)
{
	printf("a + b = %d\n",(a+b));
}

void sub(int a, int b)
{
	printf("a - b = %d\n",(a-b));
}
/*
 * 定义函数指针：数据类型 + (*函数指针名称) + (函数指针类型) -------->函数指针前面的*，可以省略
 * 例如：void(*op)(int,int) 或者 void(op)(int,int)
 * */
void operate(void(op)(int,int),int a,int b)
{
	op(a,b);
}

// 定义压缩进度回调
void call_back(int current,int tatal)
{
	printf("数据压缩进度：%d/%d\n",current,tatal);
}
// 定义压缩函数（使用函数指针）
void comparess(char* file_name,void(*callback)(int,int))
{
	callback(91,100);
}

int main()
{

	int i = 100;

	// 1.一级指针取值和取址
	printf("------------------一级指针取值与取址------------------\n");
	printf("i =  %d\n",i);
	// &i 	 表示取址操作，是内存在所在的位置
	printf("i =  %p\n",&i);
	// *(&i) 表示取值操作，取到的是指针（地址）对应 的值	
	printf("i =  %d\n",*(&i));
	// 指针（地址）变量，指针存放（指向）的就是变量的地址
	// int*  表示int类型的指针（地址）
	int *p = &i;
	printf("i =  %d\n",*p);
	// 通过指针变量修改值，此处 *p 表示 表示 i，i=200
	*p = 200;	
	printf("i =  %d\n",i);
	modifyVar(&i);
	printf("i =  %d\n",i);

	// 2. 二级指针与多级指针
	printf("------------------二级指针与多级指针------------------\n");
	int num = 12;
	int* sp = &num;		// 获取num的一级指针
	int** ssp = &sp;	// 通过一级指针获取二级指针，多级指针（就是对上一级指针进行取值）
	int*** sssp = &ssp;	// 三级指针
	printf("sp = %p, ssp = %p, sssp = %p\n",sp,ssp,sssp);

	// 通过二级指针直接获取最终的值
	printf("num 一级指针取值：%d\n",*sp);
	printf("num 二级指针取值：%d\n",**ssp);
	printf("num 三级指针取值：%d\n",***sssp);

	// 3.数组与数组指针
	printf("------------------数组和数组指针------------------\n");
	int arr[] = {1,2,3,4};
	// Linux系统中这种写法可能会出现问题
	for(int i=0; i<4; i++)
	{
		printf("%d\n",arr[i]);
	}
	// C语言的写法
	int j = 0;
	for(; j<4; j++)
	{
		printf("linux-%d\n",arr[j]);
	}
	// 数组指针 结果是：arr = &arr = &arr[0] 
	// 数组的值 = 数组的取址 = 数组第0个元素的地址
	printf("arr  = %p\n",arr);
	printf("&arr = %p\n",&arr);
	printf("&arr[0] = %p\n",&arr[0]);
	
	// 定义数组指针变量 不是int[]* 而是int*
	int* arr_p = arr;	// 数组指针指向数组的首地址

	printf("arr[0]  = %d\n",*arr_p);	// 通过对数组指针（首地址）的取值方式，获取第0个元素的值
	arr_p++;	// 指针++ 表示地址向后挪动两位，有点类似Java的下标增加
	printf("arr[1]  = %d\n",*arr_p);
	arr_p += 2; // 上次已经向后移动了一位，继续向后移动2位，到了数组最后一位
	printf("arr[4]  = %d\n",*arr_p);
	arr_p ++;	// 指针继续向后移动，超出了数组范围，但不会造成数组越界，而是到了系统分配的其他地址对应的值
	printf("arr error  = %d\n",*arr_p);
	// 移动指针到数组首地址
	arr_p -= 4;
	int k = 0;
	for(; k<4; k++)
	{
		printf("-----\n");
		// 指针移动的方式，遍历数组
		printf("arr_p -> %d\n",*(arr_p+k));
		// 直接使用数组arr进行指针移动 
		printf("arr_p -> %d\n",*(arr+k));

		// 可以通过指针取数组下标的方式，定位到指针的位置，然后对该指针进行取值，arr_p[i] = *(arr_p + i)，表示取值范围是指针移动的范围，arr_p[0]表示第0个指针位置的值
		printf("arr_p -> %d\n",arr_p[k]);
	}

	int arr_s[4];
	k = 0;
	for(; k<4; k++)
	{
		// 指针移动的方式，对数组元素赋值
		*(arr_s+k) = (4-k);
		printf("arr_s -> %d\n",arr_s[k]);
	}

	// 指针为什么有类型
	// 1.取值的时候需要数据的类型
	// 2.指针偏移的时候需要知道，指针偏移的大小
	
	// 4.函数指针（回调）
	operate(add,1,2);
	operate(sub,2,1);

	// 参数一：接收压缩文件的名称
	// 参数二：接收进度监听指针函数
	comparess("a.png",call_back);

	void(*call)(int,int);	// 定义函数指针
	call = call_back;		// 赋值函数指针
	call(71,100);			// 调用函数指针

	// 5.常量指针与指针常量（区别：常量指针是指针，指针常量是常量）
	// 常量指针 (const 在 * 之前)
	int number1 = 100;
	int number2 = 200;
	const int *num_p1 = &number1; 	// 定义一个常量指针
	num_p1 = &number2;				// 可以重新对常量指针的地址进行修改
	// *num_p1 = 300;				// 无法对常量指针对应的值进行修改

	// 指针常量 (const 在 * 之后)
	int* const num_p2 = &number2;	// 定义一个指针常量
	//num_p2 = &number1;			// 无法对指针常量的地址进行修改
	*num_p2 = 500;					// 可以重新对指针常量对应的值进行修改
	
	exit(0);

}

